package com.spacing.app;

import java.util.*;

import android.database.Cursor;
import android.util.Log;

import com.spacing.app.Tables.SpacerData;
import com.spacing.app.data.Word;

/*
 * @author Tom
 * @author Paul	
 */

public class CopyOfSpacer {
	// Spacing model variables
	private static double C = 0.25; // decay scale array to start with
	private static int LOOK_AHEAD_TIME = 15;
	private static double THRESHOLD = -0.8;
	private static double STANDARD_ALPHA = 0.32;
	
	private boolean firstSession = true;
	
	// Lists
	private SchemaHelper sh;
	private ArrayList<Word> words;

	private static long psychologicalTime = 0; // time between sessions
	private int nActivations;
	private int lastwordSeen; // index of last seen word
	private boolean lastAnswerCorrect;
	long startOfEventTime; // setter voor maken
	private long startOfSessionTime;
	private long lastWordTime;
	private long previousSessionTime;
	
	private int[] kActivations; 
	// number of activations per index (per pair) Functie voor maken?? 
	// eens
	
	private double[][] arrAlpha;
	private long[][] arrActivationsTime;
	private double[][] arrActivationValue;
	private double[][] arrDecayValuesRecentAlpha;
	
	private int numberOfPairsShown;
	// Functie voor maken?
	// eens

	public CopyOfSpacer(ArrayList<Word> words, boolean firstSession,
			SchemaHelper sh) {
		this.sh = sh;
		this.words = words;
		// set standard alpha
		
		// initialize the variables
		initVariables();
	}

	/*
	 * Gaat alle woorden af en vult de arrays kActivations,
	 * kActivationsOlderSessions en vult de decay rates
	 * TODO: arrays vervangen door arraylists oid?
	 */
	private void initVariables() {
		kActivations = new int[words.size()];
		startOfSessionTime = System.currentTimeMillis();
		
		arrAlpha = new double[words.size()][];
		// times on which the word was seen
		arrActivationsTime = new long[words.size()][];
		arrDecayValuesRecentAlpha = new double[words.size()][];
		arrActivationValue = new double[words.size()][];
		int size = 100; // willekeurig gekozen getal. Is nodig om een array
						// lengte aan te geven. Vervangen door Arraylist?
		for (int i = 0; i < words.size(); i++) {
			arrAlpha[i] = new double[size];
			arrActivationsTime[i] = new long[size * 10];
			arrDecayValuesRecentAlpha[i] = new double[size * 10];
			arrActivationValue[i] = new double[size * 10];
			arrAlpha[i][0] = 0.25;
			kActivations[i] = 0;
		}
		
		getSpacerData();
		
	}

	
	/*
	 * method that is called to when an answer is given. 
	 * TODO: code opschonen
	 */
	public void updateAnswer(boolean isCorrect, long time) {
		long activationTime;
		
		/*
		 * penalty voor fout antwoord. Moet op betere manier ingebouwd worden (alpha?)
		 */
		if (!isCorrect && kActivations[lastwordSeen] != 0) { 
			kActivations[lastwordSeen]--;
		}
		
		nActivations = kActivations[lastwordSeen];
		lastAnswerCorrect = isCorrect;
		if (isCorrect) {
			activationTime = time - startOfSessionTime + psychologicalTime;
		} else {
			activationTime = startOfEventTime - startOfSessionTime + psychologicalTime;
		}
		arrActivationsTime[lastwordSeen][nActivations] = activationTime;
		double temp_alpha = getAlpha(lastwordSeen, (activationTime / 1000),
				STANDARD_ALPHA);
		arrAlpha[lastwordSeen][nActivations] = temp_alpha;
		calculateNewDecayValues(lastwordSeen,
				arrAlpha[lastwordSeen][nActivations]);
		double acti = getSpacingActivation(lastwordSeen,
				(activationTime / 1000),
				arrAlpha[lastwordSeen][nActivations]);
		if (nActivations == 0) {
			arrActivationValue[lastwordSeen][nActivations] = -1000;
			// arrActivationsOnRtWithOlderSessions[last_word_seen][(kActivations[last_word_seen]+kActivationsOlderSessions[last_word_seen])]=
			// -1000;
		} else {
			arrActivationValue[lastwordSeen][nActivations] = acti;
		}
		// Set ready for next rehearsal
		kActivations[lastwordSeen]++;
		
		new Thread(new Runnable() {
		    public void run() {
		    	sh.addSpacerData(words.get(lastwordSeen).getWord1(),System.currentTimeMillis(), arrAlpha[lastwordSeen][nActivations],arrActivationsTime[lastwordSeen][nActivations],arrActivationValue[lastwordSeen][nActivations],arrDecayValuesRecentAlpha[lastwordSeen][nActivations]);
		    }
		  }).start();
		
		
	}// updateAnswer

	// calculates optimal alpha. Doet nog geen fuck Yehees
	private double getAlpha(int index, long activation_time, double alpha) {
		double result = alpha;
		return result;
	}

	

	/*
	 * Real model functions. Function to recalculate new decay values after a pair has been seen
	 */ 
	public void calculateNewDecayValues(int index, double alpha) {

		double[] activations;
		if (kActivations[index] > 0) {
			activations = new double[kActivations[index] + 1];
		} else {
			activations = new double[1];
		}

		double[] encounters = new double[kActivations[index]];

		for (int i = 0; i <= kActivations[index]; i++) {
			if (i > 0) {
				activations[i] = getActivation(encounters,
						(arrActivationsTime[index][i] / 1000),
						arrDecayValuesRecentAlpha[index], i);
				if (i < encounters.length) {
					encounters[i] = (arrActivationsTime[index][i] / 1000);
				}
				arrDecayValuesRecentAlpha[index][i] = getDecay(activations[i],
						alpha);
			} else {
				activations[i] = -1000;
				arrDecayValuesRecentAlpha[index][i] = getDecay(activations[i],
						alpha);
				if (encounters.length != 0) {
					encounters[i] = (arrActivationsTime[index][i] / 1000);
				}
			}
		}
	} // calculateNewDecayValues

	/*
	 * SpacingActivation returns recent activation value of a pair uses
	 * activation times and decay rates
	 * 
	 * @param: wordnumber index, current time
	 */
	double getSpacingActivation(int index, long time, double alpha) {
		double activationValue = 0;
		double sum = 0;
		double[] encounters = new double[kActivations[index]];
		for (int i = 0; i < kActivations[index]; i++) {
			encounters[i] = (arrActivationsTime[index][i] / 1000);
			sum = sum + Math.pow((time - encounters[i]),-arrDecayValuesRecentAlpha[index][i]);
		}
		activationValue = Math.log(sum);
		return activationValue;

	}// getSpacingActivation

	/*
	 * calculate activation value based on the timestamps of encounters, current
	 * time, decay values returns an activation value an item for the current
	 * time (time), high activation means that recall will be accurate and fast.
	 * Activation is based on the summation of the individual practive events. -
	 * encounter contains an array of timestamps on which the item was
	 * encountered - time is the current time - decays contains an array of
	 * decay values for each encounter - s is the index: if s is 2 activation is
	 * calculated for the second encounter
	 */
	double getActivation(double[] encounters, long time, double[] decays, int s) {
		double sum = 0;
		double activation = 0;
		for (int i = 0; i < s; i++) {
			sum = (sum + Math.pow(((time - encounters[i])), -decays[i]));
		}
		activation = Math.log(sum);
		return activation;
	}// getActivation

	// calculate decay value based on the activation and the alfa value
	// (constant and also the minimum decay value)
	double getDecay(double activation, double alpha) {
		double decay = C * Math.exp(activation) + alpha;
		return decay;
	}// get decay

	/*
	 * determines the next pair looks for the the pair with the lowest
	 * activation (excluding new pairs) based on the lookahead time the methods
	 * checks if the activation will be below the threshold. if so then this is
	 * the next pair. if none of the previously seen pairs comes below the
	 * threshold and there are new pairs then select a new pair. If everything
	 * is seen at least once, then select the pair with the oldest repetition
	 */
	int determineNextPair() {
		int nextPair = 0;
		double lowestActivation = Double.MAX_VALUE;
		double activationValue;
		//double previousAlpha;
		int index = 0;
		long time = (System.currentTimeMillis() - startOfSessionTime
				+ psychologicalTime + LOOK_AHEAD_TIME * 1000) / 1000;

		
		
		/*************************** Spacing Method **********************************************************/

		double[] arrTempAcValue = new double[words.size()];
		for (int i = 0; i < words.size(); i++) {
			if(kActivations[i]>0){
				// calculate activation
				double previousAlpha = arrAlpha[i][(kActivations[i] - 1)];
				// if last time already shown and
				if ((lastwordSeen == i) && (lastAnswerCorrect)) {
					activationValue = 100;
				} else {
					activationValue = getSpacingActivation(i, time, previousAlpha);
					arrTempAcValue[i] = activationValue;
				}
				
				if (activationValue < lowestActivation) {
					lowestActivation = activationValue;
					index = i;
				}
			}
		}
		
		
		
		
		
/*		double[] arrTempAcValue = new double[words.size()];
		for (int i = 0; i < words.size(); i++) {
			if ((kActivations[i]) > 0) { // pair already seen, exclude new items
				// calculate activation
				temp_alpha = arrAlpha[i][(kActivations[i] - 1)];
				// if last time already shown and
				if ((last_word_seen == i) && (last_answer_correct)) {
					activation_value = 100;
				} else {
					activation_value = getSpacingActivation(i, time, temp_alpha);
					arrTempAcValue[i] = activation_value;
				}
				if (activation_value < lowest_activation) {
					lowest_activation = activation_value;
					index = i;
				}
			}
		}*/
		
		
		if (lowestActivation < THRESHOLD) {
			nextPair = index; // repeat the word pair with the lowest activation
								// (at.. seconds from now)

		} else if (unshownPairs()) { // no activation
															// below threshold
			/*
			if (firstSession) {
				next_pair = number_of_pairs_shown;
				number_of_pairs_shown++;
			} else {
			*/
				
				for (int i = 0; i < words.size(); i++) {
					if (kActivations[i] == 0) {// word i already seen, exclude
												// new items
						nextPair = i;
						lastwordSeen = nextPair;
						//numberOfPairsShown++;
						return nextPair;
					}
				}
		} else { // all words are shown and activation > Threshold
			nextPair = oldestRepetition();
		}
		lastwordSeen = nextPair;
		return nextPair;
	}// determineNextPair
	
	
	/*
	 * returns true if there are still unshown pairs
	 */
	private boolean unshownPairs(){
		for(int i =0; i< words.size();i++){
			if(kActivations[i]==0){
				return true;
			}
		}
		return false;
	}
	
	/*
	 * returns the chunk that has the oldest repetition
	 */
	private int oldestRepetition() {
		int oldestPair = 0;
		long oldestRep = Long.MAX_VALUE;
		long at;
		for (int i = 0; i < words.size(); i++) {
			if(kActivations[i]>0){
				at = arrActivationsTime[i][(kActivations[i] - 1)];
				if (at < oldestRep) {
					oldestRep = at;
					oldestPair = i;
				}
			}
		}
		return oldestPair;
	}

	private void getSpacerData(){
		for(int i = 0; i< words.size();i++){
			getWordData(i);
		}
		if(!firstSession){
			psychologicalTime = startOfSessionTime - previousSessionTime;
		}
		startOfSessionTime = startOfSessionTime - lastWordTime;
	}
	
		
	
	private void getWordData(int wordIndex){
		Cursor c = sh.getEncounters(words.get(wordIndex).getWord1());
				
		while (c.moveToNext()) {
			int colidAbsTime = c.getColumnIndex(SpacerData.ABSTIME);
			int colidAlpha = c.getColumnIndex(SpacerData.ALPHA);
			int colidTime = c.getColumnIndex(SpacerData.TIME);
			int colidValue = c.getColumnIndex(SpacerData.VALUE);
			int colidDecay = c.getColumnIndex(SpacerData.DECAY);
			arrAlpha[wordIndex][c.getPosition()] = c.getDouble(colidAlpha);
			arrActivationsTime[wordIndex][c.getPosition()] = c.getLong(colidTime);
			arrActivationValue[wordIndex][c.getPosition()] = c.getDouble(colidValue);
			arrDecayValuesRecentAlpha[wordIndex][c.getPosition()] = c.getDouble(colidDecay);
			
			kActivations[wordIndex]++;
			
			//numberOfPairsShown = wordIndex+1;
			firstSession = false;
			
			
			long st = c.getLong(colidAbsTime);
			if(st > previousSessionTime){
				previousSessionTime = st;
			}
			
			
			long t = c.getLong(colidTime);
			System.out.println(t);
			if ( lastWordTime < t ) {
				lastWordTime = t;
			}
		}
	}
	

	
}// SpacingMethods
